package com.zgh.opcua.manager;

import com.zgh.opcua.entity.WriteNodeEntity;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @ClassName KepwareWriteManager
 * @Description Opc写入管理器
 * @Author Administrator
 * @Date 17:47 2025/11/5
 * @Version 1.0
 */
@Slf4j
public class KepwareWriteManager {


    private final int maxRetryCount = 3;  // 最大重试次数
    private final long retryInterval = 2000;  // 重试间隔，单位：毫秒
    private final int batchSize = 100;  // 每批次写入节点数量（用于批量写入）

    /**
     * 异步写入单个节点
     *
     * @param node 节点信息
     * @return CompletableFuture 返回写入结果
     */
    public CompletableFuture<String> writeAsync(WriteNodeEntity node) {
        return CompletableFuture.supplyAsync(() -> {
            return writeWithRetry(node, maxRetryCount);
        });
    }

    /**
     * 写入节点并进行重试处理
     *
     * @param node    节点
     * @param retries 剩余重试次数
     * @return 写入结果
     */
    private String writeWithRetry(WriteNodeEntity node, int retries) {
        try {
            OpcUaClient opcUaClient = OpcUaClientInstance.getInstance().getOpcUaClient();
            // 创建节点 ID 和 Variant 值
            NodeId nodeId = new NodeId(node.getIndex(), node.getIdentifier());
            Variant value = getVariantValue(node);
            if (value == null) {
                return "节点【" + node.getIdentifier() + "】的值类型不支持：" + node.getType();
            }

            // 写入操作
            DataValue dataValue = new DataValue(value, null, null);
            StatusCode statusCode = opcUaClient.writeValue(nodeId, dataValue).get();

            // 返回写入状态
            return "节点【" + node.getIdentifier() + "】写入状态：" + statusCode.isGood();
        } catch (Exception e) {
            if (retries > 0) {
                log.warn("写入节点【{}】失败，正在进行第 {} 次重试...", node.getIdentifier(), maxRetryCount - retries + 1);
                try {
                    Thread.sleep(retryInterval);  // 等待重试间隔
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
                return writeWithRetry(node, retries - 1);  // 递归重试
            } else {
                log.error("写入节点【{}】失败，已达到最大重试次数", node.getIdentifier());
                return "节点【" + node.getIdentifier() + "】写入失败：" + e.getMessage();
            }
        }
    }


    /**
     * 异步批量写入节点
     *
     * @param nodes 节点列表
     * @return CompletableFuture 列表，包含每个节点写入的结果
     */
    public CompletableFuture<List<String>> writeBatchAsync(List<WriteNodeEntity> nodes) {
        List<CompletableFuture<String>> futures = new ArrayList<>();

        // 将节点列表拆分为多个批次进行并行写入
        for (int i = 0; i < nodes.size(); i += batchSize) {
            List<WriteNodeEntity> batch = nodes.subList(i, Math.min(i + batchSize, nodes.size()));
            futures.add(CompletableFuture.supplyAsync(() -> {
                List<String> results = new ArrayList<>();
                for (WriteNodeEntity node : batch) {
                    results.add(writeWithRetry(node, maxRetryCount));
                }
                return String.join(", ", results);
            }));
        }

        // 返回所有写入操作的结果
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .thenApply(v -> futures.stream()
                        .map(CompletableFuture::join)
                        .collect(Collectors.toList()));
    }

    /**
     * 根据节点类型生成对应的 Variant 值
     *
     * @param node 节点信息
     * @return Variant 类型的值
     */
    private Variant getVariantValue(WriteNodeEntity node) {
        if (node.getValue() == null) {
            return null;
        }

        switch (node.getType().toLowerCase()) {
            case "int":
                return new Variant(Integer.parseInt(node.getValue().toString()));
            case "boolean":
                return new Variant(Boolean.parseBoolean(node.getValue().toString()));
            case "double":
                return new Variant(Double.parseDouble(node.getValue().toString()));
            case "string":
                return new Variant(node.getValue().toString());
            default:
                log.warn("不支持的节点类型: {}", node.getType());
                return null;  // 如果类型不支持，返回 null
        }
    }

    public static void main(String[] args) throws Exception {
        // 创建 OpcUaClient（假设已连接）
        OpcUaClient client = OpcUaClientInstance.getInstance().getOpcUaClient("opc.tcp://192.168.40.30:49320", "None", null, null);
        client.connect().get();
        // 创建写入管理器，配置最大重试次数、重试间隔、批量大小
        KepwareWriteManager writeManager = new KepwareWriteManager();

        // 创建需要写入的节点列表
        List<WriteNodeEntity> nodes = Arrays.asList(
                WriteNodeEntity.builder().index(2).identifier("Channel1.Device1.Tag1").value(100).type("int").build(),
                WriteNodeEntity.builder().index(2).identifier("Channel1.Device1.Tag2").value(true).type("boolean").build(),
                WriteNodeEntity.builder().index(2).identifier("Channel1.Device1.Tag3").value(45.67).type("double").build()
        );

        // 执行批量写入
        CompletableFuture<List<String>> batchResult = writeManager.writeBatchAsync(nodes);

        // 等待批量写入完成并输出结果
        batchResult.thenAccept(results -> results.forEach(System.out::println));
    }

}
